//  This file is part of NHibernate.ReLinq.Sample a sample showing
//  the use of the open source re-linq library to implement a non-trivial 
//  Linq-provider, on the example of NHibernate (www.nhibernate.org).
//  Copyright (C) 2005-2009 rubicon informationstechnologie gmbh, www.rubicon.eu
//  
//  NHibernate.ReLinq.Sample is based on re-motion re-linq (http://www.re-motion.org/).
//  
//  NHibernate.ReLinq.Sample is free software; you can redistribute it 
//  and/or modify it under the terms of the MIT License 
// (http://www.opensource.org/licenses/mit-license.php).
// 
using System;
using System.Linq;
using System.Linq.Expressions;
using Remotion.Linq;
using Remotion.Linq.Parsing.Structure;
using ScrappyDB.BaseClasses;
using ScrappyDB.Utilities;

namespace ScrappyDB.Linq
{
    /// <summary>
    /// Provides the main entry point to a LINQ query.
    /// </summary>
    public class SdbSet<T> : QueryableBase<T> where T : class, new()
    {
        private static IQueryExecutor CreateExecutor()
        {
            return new SdbQueryExecutor();
        }

        private static IQueryParser CreateParser()
        {
            return QueryParser.CreateDefault();
        }



        // This constructor is called by our users, create a new IQueryExecutor.
        public SdbSet()
            : base(CreateParser(), CreateExecutor())
        {
        }

        // This constructor is called indirectly by LINQ's query methods, just pass to base.
        public SdbSet(IQueryProvider provider, Expression expression)
            : base(provider, expression)
        {
        }

        public T Find(params object[] keyValues)
        {
            if(keyValues == null || keyValues.Count() == 0)
                throw new ArgumentNullException();

            //TODO:
            if (keyValues.Count() > 1)
                throw new NotImplementedException("FIX ME!");

            var db = new Db();
            return db.Find<T>(keyValues[0]);
        }

        public void Add(T newEntity)
        {
            if (typeof(T).IsSubclassOf(typeof(SdbEntity)))
            {
                var dirtyProperty = typeof(T).GetProperty("Dirty");
                dirtyProperty.SetValue(newEntity, true, null);
            }

            LocalContext.Add(newEntity);
        }

        /// <summary>
        /// Removes entities from the local context.  They are NOT deleted from the database until you call SaveChanges!
        /// </summary>
        /// <param name="deleteEntity"></param>
        public void Remove(T deleteEntity)
        {
            LocalContextDeleted.Add(deleteEntity);

            foreach (var entity in LocalContext)
            {
                //we need to do this because deleteEntity may be a different instance of the same record
                if (EntityHelper.GetEncodedItemNameValue(entity) == EntityHelper.GetEncodedItemNameValue(deleteEntity))
                {
                    LocalContext.Remove(entity);
                    break;
                }
            }

        }

        /// <summary>
        /// Saves pending Adds/Updates/Deletes to all entities in this SdbSet
        /// </summary>
        public void SaveChanges()
        {
            if(LocalContextDeleted != null && LocalContextDeleted.Count > 0)
            {
                    var db = new Db();
                    db.Delete<T>(LocalContextDeleted);
            }

            LocalContext.SaveChanges();
        }
    }
}